home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 November / EnigmA AMIGA RUN 02 (1995)(G.R. Edizioni)(IT)[!][issue 1995-11][Skylink CD].iso / earcd / program / misc / bgui12.lha / demos / BGUIPlayer / CDCode.c < prev    next >
C/C++ Source or Header  |  1995-07-22  |  16KB  |  541 lines

  1. /*
  2.  *    CDCODE.C
  3.  */
  4.  
  5. #include "BGUIPlayer.h"
  6.  
  7. /*
  8.  *    Export data.
  9.  */
  10. Prototype UWORD  Status, Track;
  11. Prototype UBYTE  TOCNumTracks, CDID[ 20 ];
  12.  
  13. UBYTE               *SCSIData, *TOCBuffer, *SCSISense, *SCSIDevice;
  14. struct MsgPort           *SCSIPort;
  15. struct IOStdReq        *SCSIRequest;
  16. ULONG            SCSIError, SCSIUnit;
  17. struct SCSICmd        SCSICommand;
  18.  
  19. UWORD         Status;                /* Current drive status.     */
  20. UWORD         Track;                                 /* Current audio track.          */
  21. UBYTE         TOCNumTracks;                /* Number of available tracks.     */
  22. UBYTE         TOCFlags[ 256 ];            /* 1 = Data, 0 = Audio         */
  23. ULONG         TOCTrackAddr[ 256 ];            /* Starting adresses of tracks.  */
  24. UBYTE         CDID[ 20 ];                /* File name for the disk.     */
  25.  
  26. /*
  27.  *    Setup SCSI muck.
  28.  */
  29. Prototype BOOL SetupSCSI( UBYTE *, ULONG );
  30.  
  31. BOOL SetupSCSI( UBYTE *dev_name, ULONG dev_id )
  32. {
  33.     UBYTE            *data;
  34.  
  35.     /*
  36.      *    Save device and unit.
  37.      */
  38.     SCSIDevice = dev_name;
  39.     SCSIUnit   = dev_id;
  40.  
  41.     /*
  42.      *    Allocate Sense, Data and TOC buffers.
  43.      */
  44.     if ( data = ( UBYTE * )AllocVec( MAX_DATA_LEN + MAX_TOC_LEN + SENSE_LEN, MEMF_CHIP | MEMF_CLEAR )) {
  45.         /*
  46.          *    Setup pointers.
  47.          */
  48.         SCSIData  = data;
  49.         SCSISense = data + MAX_DATA_LEN;
  50.         TOCBuffer = SCSISense + MAX_TOC_LEN;
  51.         /*
  52.          *    Create port and request.
  53.          */
  54.         if ( SCSIPort = CreateMsgPort()) {
  55.             if ( SCSIRequest = ( struct IOStdReq * )CreateIORequest( SCSIPort, sizeof( struct IOStdReq ))) {
  56.                 /*
  57.                  *    Open the device.
  58.                  */
  59.                 if ( SCSIError = OpenDevice( dev_name, dev_id, ( struct IORequest * )SCSIRequest, 0 )) {
  60.                     /*
  61.                      *    Error opening the device.
  62.                      */
  63.                     ReportError( "_OK", "Unable to open %s (Error %ld).", dev_name, SCSIError );
  64.                 } else
  65.                     return( TRUE );
  66.             } else
  67.                 ReportError( "_OK", "Unable to create IO request." );
  68.         } else
  69.             ReportError( "_OK", "Unable to create message port." );
  70.     } else
  71.         ReportError( "_OK", "Out of memory." );
  72.  
  73.     EndSCSI();
  74.     return( FALSE );
  75. }
  76.  
  77. /*
  78.  *    Close down the SCSI muck.
  79.  */
  80. Prototype VOID EndSCSI( void );
  81.  
  82. VOID EndSCSI( void )
  83. {
  84.     if ( ! SCSIError ) CloseDevice(( struct IORequest * )SCSIRequest );
  85.     if ( SCSIRequest ) DeleteIORequest(( struct IORequest * )SCSIRequest );
  86.     if ( SCSIPort     ) DeleteMsgPort( SCSIPort );
  87.     if ( SCSIData     ) FreeVec( SCSIData );
  88. }
  89.  
  90. /*
  91.  *    Do a SCSI command.
  92.  */
  93. Prototype ULONG DoSCSICmd( UBYTE *, ULONG, UBYTE *, ULONG, UBYTE );
  94.  
  95. ULONG DoSCSICmd( UBYTE *data, ULONG datasize, UBYTE *cmd, ULONG cmdsize, UBYTE flags )
  96. {
  97.     SCSIRequest->io_Length        = sizeof( struct SCSICmd );
  98.     SCSIRequest->io_Data        = ( APTR )&SCSICommand;
  99.     SCSIRequest->io_Command         = HD_SCSICMD;
  100.  
  101.     SCSICommand.scsi_Data        = ( APTR )data;
  102.     SCSICommand.scsi_Length         = datasize;
  103.     SCSICommand.scsi_SenseActual    = 0;
  104.     SCSICommand.scsi_SenseData    = SCSISense;
  105.     SCSICommand.scsi_SenseLength    = SENSE_LEN;
  106.     SCSICommand.scsi_Command    = cmd;
  107.     SCSICommand.scsi_CmdLength    = cmdsize;
  108.     SCSICommand.scsi_Flags        = flags;
  109.  
  110.     DoIO(( struct IORequest * )SCSIRequest );
  111.  
  112.     return( SCSIRequest->io_Error );
  113. }
  114.  
  115. /*
  116.  *    Make sure the device is a CD-ROM player.
  117.  */
  118. Prototype BOOL SCSI_IsCDRom( void );
  119.  
  120. BOOL SCSI_IsCDRom( void )
  121. {
  122.     static struct SCSICmd6    command = { SCSI_CMD_INQ, 0, 0, 0, 0, 0 };
  123.  
  124.     command.B4 = MAX_DATA_LEN;
  125.  
  126.     if ( ! DoSCSICmd(( UBYTE * )SCSIData, MAX_DATA_LEN, ( UBYTE * )&command, sizeof( command ), SCSIF_READ | SCSIF_AUTOSENSE )) {
  127.         /*
  128.          *    This must result in 5 otherwise
  129.          *    the drive at this address is not
  130.          *    a CD-ROM player.
  131.          */
  132.         return((( SCSIData[ 0 ] & 0x1F ) == 5 ) ? TRUE : FALSE );
  133.     }
  134.     return( FALSE );
  135. }
  136.  
  137. /*
  138.  *    Show the device it's Inquiry data.
  139.  */
  140. Prototype VOID SCSI_Inquire( void );
  141.  
  142. /*
  143.  *    ANSI standards.
  144.  */
  145. static UBYTE *ANSIStr[] = {
  146.     "The device might or might not comply to an ANSI-approved standard.",
  147.     "The device complies to ANSI X3.131-1986 (SCSI-1).",
  148.     "The device complies to (SCSI-2)."
  149. };
  150.  
  151. VOID SCSI_Inquire( void )
  152. {
  153.     static struct SCSICmd6    command = { SCSI_CMD_INQ, 0, 0, 0, 0, 0 };
  154.  
  155.     command.B4 = MAX_DATA_LEN;
  156.  
  157.     if ( ! DoSCSICmd(( UBYTE * )SCSIData, MAX_DATA_LEN, ( UBYTE * )&command, sizeof( command ), SCSIF_READ | SCSIF_AUTOSENSE )) {
  158.         /*
  159.          *    Show the data read from the device.
  160.          */
  161.         ReportError( "_OK", ISEQ_C "Software Device: %s\nUnit: %ld\n\n"
  162.                     "Type (5): CD-ROM device\n"
  163.                     "ANSI version: %ld, %s\n"
  164.                     "Vendor: " ISEQ_B "%.8s" ISEQ_N "\n"
  165.                     "Product: " ISEQ_B "%.16s" ISEQ_N "\n"
  166.                     "Revision: " ISEQ_B "%.4s",
  167.                     SCSIDevice,
  168.                     SCSIUnit,
  169.                     SCSIData[ 2 ] & 0x07, ANSIStr[ SCSIData[ 2 ] & 0x07 ],
  170.                     &SCSIData[ 8 ],
  171.                     &SCSIData[ 16 ],
  172.                     &SCSIData[ 32 ] );
  173.     }
  174. }
  175.  
  176. /*
  177.  *    Read the TOC from the device.
  178.  */
  179. Prototype VOID SCSI_ReadTOC( void );
  180.  
  181. VOID SCSI_ReadTOC( void )
  182. {
  183.     static struct SCSICmd10 command = { SCSI_CMD_READTOC, 0, 0, 0, 0, 0, 0, 0x03, 0x24, 0 };
  184.     WORD            tocsize, i;
  185.     UBYTE               *tocptr;
  186.  
  187.     TOCNumTracks = 0;
  188.  
  189.     if ( ! DoSCSICmd(( UBYTE * )TOCBuffer, MAX_TOC_LEN, ( UBYTE * )&command,  sizeof( command ), SCSIF_READ | SCSIF_AUTOSENSE )) {
  190.         /*
  191.          *    Get size of TOC.
  192.          */
  193.         tocsize = ( TOCBuffer[ 0 ] << 8 ) + TOCBuffer[ 1 ];
  194.         TOCTrackAddr[ 2 ] = 0;
  195.         TOCNumTracks      = 0;
  196.         if ( tocsize >= 2 ) tocsize -= 2;
  197.         /*
  198.          *    Copy track addresses.
  199.          */
  200.         for ( tocptr = &TOCBuffer[ 4 ]; tocptr < ( &TOCBuffer[ 4 ] + tocsize ); tocptr += 8 ) {
  201.             TOCTrackAddr[ TOCNumTracks ] = ( tocptr[ 4 ] << 24 ) | ( tocptr[ 5 ] << 16 ) | ( tocptr[ 6 ] << 8 ) | ( tocptr[ 7 ] );
  202.             /*
  203.              *    Data or audio track.
  204.              */
  205.             TOCFlags[     TOCNumTracks ] = ( tocptr[ 1 ] & 0x04 ) ? 1 : 0;
  206.             TOCNumTracks++;
  207.         }
  208.         TOCNumTracks--;
  209.     }
  210.  
  211.     /*
  212.      *    Build CD identification. This string is used
  213.      *    for the file name of the disk-files. Usually this
  214.      *    should be unique enough. I do not use the CD PIN-Code
  215.      *    since it seems that not all disks have one.
  216.      *
  217.      *    This is also the way MCDP by Boris Jakubaschk does it.
  218.      */
  219.     sprintf( CDID, "%02ld%06lx%06lx", TOCNumTracks, TOCTrackAddr[ 2 ], TOCTrackAddr[ TOCNumTracks ] );
  220.     /*
  221.      *    Read disk file.
  222.      */
  223.     LoadDiskFile();
  224.     /*
  225.      *    Total disk time.
  226.      */
  227.     TotalIDA = TOCTrackAddr[ TOCNumTracks ] / 75 / 60;
  228.     TotalIDB = ( TOCTrackAddr[ TOCNumTracks ] / 75 ) % 60;
  229.     SetGadgetAttrs(( struct Gadget * )GO_TotalA, Player, NULL, INDIC_Level, TotalIDA, TAG_END );
  230.     SetGadgetAttrs(( struct Gadget * )GO_TotalB, Player, NULL, INDIC_Level, TotalIDB, TAG_END );
  231.     /*
  232.      *    Enable/Disable track buttons.
  233.      */
  234.     for ( i = 0; i < 20; i++ )
  235.         SetGadgetAttrs(( struct Gadget * )TrackButtons[ i ], Player, NULL, GA_Disabled, i < TOCNumTracks ? FALSE : TRUE, TAG_END );
  236.     /*
  237.      *    Disk and Artist name.
  238.      */
  239.     if ( Status != SCSI_STAT_NO_DISK ) {
  240.         SetGadgetAttrs(( struct Gadget * )GO_Title,     Player, NULL, INFO_TextFormat, DiskName, TAG_END );
  241.         SetGadgetAttrs(( struct Gadget * )GO_TrackTitle, Player, NULL, INFO_TextFormat, Artist,   TAG_END );
  242.     }
  243.     /*
  244.      *    Enable Edit CD menu.
  245.      */
  246.     if ( WO_Player ) {
  247.         if ( TOCNumTracks )
  248.             DisableMenu( WO_Player, ID_EDIT, FALSE );
  249.     }
  250. }
  251.  
  252. /*
  253.  *    Play a audio track.
  254.  */
  255. Prototype VOID SCSI_PlayAudio( WORD );
  256.  
  257. VOID SCSI_PlayAudio( WORD starttrack )
  258. {
  259.     static struct SCSICmd12 command = { SCSI_CMD_PLAYAUDIO12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  260.     WORD            i = 1;
  261.     ULONG            addr;
  262.  
  263.     /*
  264.      *    Force an existing audio track.
  265.      */
  266.     while (( TOCFlags[ starttrack - 1 ] == 1 ) && ( starttrack < TOCNumTracks )) starttrack++;
  267.  
  268.     /*
  269.      *    Start address of the track.
  270.      */
  271.     addr = TOCTrackAddr[ starttrack - 1 ] + 1;
  272.  
  273.     command.B2 = ( addr & 0xFF000000 ) >> 24;
  274.     command.B3 = ( addr & 0x00FF0000 ) >> 16;
  275.     command.B4 = ( addr & 0x0000FF00 ) >> 8;
  276.     command.B5 = ( addr & 0x000000FF );
  277.  
  278.     /*
  279.      *    End address of the disk.
  280.      */
  281.     addr = TOCTrackAddr[ TOCNumTracks ] - TOCTrackAddr[ starttrack - 1 ] - 1;
  282.  
  283.     command.B6 = ( addr & 0xFF000000 ) >> 24;
  284.     command.B7 = ( addr & 0x00FF0000 ) >> 16;
  285.     command.B8 = ( addr & 0x0000FF00 ) >> 8;
  286.     command.B9 = ( addr & 0x000000FF );
  287.  
  288.     DoSCSICmd(( UBYTE * )SCSIData, MAX_DATA_LEN, ( UBYTE * )&command, sizeof( command ), SCSIF_READ | SCSIF_AUTOSENSE );
  289. };
  290.  
  291. /*
  292.  *    Pause continue playing.
  293.  */
  294. Prototype VOID SCSI_PauseResume( void );
  295.  
  296. VOID SCSI_PauseResume( void )
  297. {
  298.     static struct SCSICmd10 command = { SCSI_CMD_PAUSERESUME, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  299.  
  300.     command.B8 = ( Status == SCSI_STAT_PLAYING ) ? 0x00 : 0x01;
  301.  
  302.     DoSCSICmd( 0, 0, ( UBYTE * )&command, sizeof( command ), SCSIF_READ | SCSIF_AUTOSENSE );
  303. }
  304.  
  305. /*
  306.  *    Open drive door.
  307.  */
  308. Prototype VOID SCSI_Eject( void );
  309.  
  310. VOID SCSI_Eject( void )
  311. {
  312.     static struct SCSICmd6 command = { SCSI_CMD_SSU, 0, 0, 0, 0, 0 };
  313.  
  314.     command.B4 = 2;
  315.  
  316.     DoSCSICmd( 0, 0, ( UBYTE * )&command, sizeof( command ), SCSIF_READ | SCSIF_AUTOSENSE );
  317. }
  318.  
  319. /*
  320.  *    Stop playing.
  321.  */
  322. Prototype VOID SCSI_Stop( void );
  323.  
  324. VOID SCSI_Stop( void )
  325. {
  326.     static struct SCSICmd6 command = { SCSI_CMD_SSU, 0, 0, 0, 0, 0 };
  327.  
  328.     command.B4 = 0;
  329.  
  330.     DoSCSICmd( 0, 0, ( UBYTE * )&command, sizeof( command ), SCSIF_READ | SCSIF_AUTOSENSE );
  331. }
  332.  
  333. /*
  334.  *    Adjust output volume.
  335.  */
  336. Prototype VOID SCSI_SetVolume( WORD, WORD, WORD, WORD );
  337.  
  338. VOID SCSI_SetVolume( WORD vol0, WORD vol1, WORD vol2, WORD vol3 )
  339. {
  340.     WORD            i, j;
  341.     static struct SCSICmd6    modecommand;
  342.     static struct volmodedata {
  343.         UBYTE head[4];
  344.         UBYTE page;      /* page code 0x0E                    */
  345.         UBYTE plength;      /* page length                    */
  346.         UBYTE b2;      /* bit 2: Immed, bit 1: SOTC                */
  347.         UBYTE b3;      /* reserved                        */
  348.         UBYTE b4;      /* reserved                        */
  349.         UBYTE b5;      /* bit 7: APRVal, bit 3-0: format of LBAs / Sec.    */
  350.         UWORD bps;      /* logical blocks per second audio playback        */
  351.         UBYTE out0;      /* lower 4 bits: output port 0 channel selection    */
  352.         UBYTE vol0;      /* output port 0 volume                */
  353.         UBYTE out1;      /* lower 4 bits: output port 1 channel selection    */
  354.         UBYTE vol1;      /* output port 1 volume                */
  355.         UBYTE out2;      /* lower 4 bits: output port 2 channel selection    */
  356.         UBYTE vol2;      /* output port 2 volume                */
  357.         UBYTE out3;      /* lower 4 bits: output port 3 channel selection    */
  358.         UBYTE vol3;      /* output port 3 volume                */
  359.     } modedata;
  360.  
  361.     for ( i = 0; i < 4; i++ ) modedata.head[i] = 0;
  362.  
  363.     modecommand.Opcode    = SCSI_CMD_MSE;
  364.     modecommand.B1          = 0;
  365.     modecommand.B2          = 0x0E;
  366.     modecommand.B3          = 0;
  367.     modecommand.B4          = MAX_DATA_LEN;
  368.     modecommand.Control   = 0;
  369.  
  370.     if ( DoSCSICmd(( UBYTE * )SCSIData, MAX_DATA_LEN, ( UBYTE * )&modecommand, sizeof( modecommand ), SCSIF_READ | SCSIF_AUTOSENSE ))
  371.         return;
  372.  
  373.     for ( j = ( SCSIData[ 0 ] + 1 ), i = SCSIData[ 3 ] + 4; i < j; i += SCSIData[ i + 1 ] + 2 )
  374.         memcpy( &modedata.page, &SCSIData[ i ], 16 );
  375.  
  376.     modedata.page        = 0x0E;
  377.     modedata.plength    = 0x0E;
  378.  
  379.     if ( vol0 >= 0 ) modedata.vol0 = vol0;
  380.     if ( vol1 >= 0 ) modedata.vol1 = vol1;
  381.     if ( vol2 >= 0 ) modedata.vol2 = vol2;
  382.     if ( vol3 >= 0 ) modedata.vol3 = vol3;
  383.  
  384.     modecommand.Opcode      = SCSI_CMD_MSL;
  385.     modecommand.B1          = 0x10;
  386.     modecommand.B2          = 0;
  387.     modecommand.B3          = 0;
  388.     modecommand.B4          = sizeof( modedata );
  389.     modecommand.Control      = 0;
  390.  
  391.     DoSCSICmd(( UBYTE * )&modedata, sizeof( modedata ), ( UBYTE * )&modecommand, sizeof( modecommand ), SCSIF_WRITE | SCSIF_AUTOSENSE );
  392. }
  393.  
  394. /*
  395.  *    Jump "blocks" frames.
  396.  */
  397. Prototype void SCSI_Jump( WORD );
  398.  
  399. void SCSI_Jump( WORD blocks )
  400. {
  401.     static struct SCSICmd10 command1 = { SCSI_CMD_READSUBCHANNEL, 0, 0x40, 0, 0, 0, 0, 0, 0, 0 };
  402.     static struct SCSICmd12 command2 = { SCSI_CMD_PLAYAUDIO12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  403.     ULONG            addr;
  404.  
  405.     command1.B2 = 0x40;
  406.     command1.B3 = 1;
  407.     command1.B6 = 0;
  408.     command1.B7 = 255;
  409.     command1.B8 = 255;
  410.  
  411.     if ( ! DoSCSICmd(( UBYTE * )SCSIData, MAX_DATA_LEN, ( UBYTE * )&command1, sizeof( command1 ), SCSIF_READ | SCSIF_AUTOSENSE)) {
  412.  
  413.         addr  = ( SCSIData[ 8 ] << 24 ) | ( SCSIData[ 9 ] << 16 ) | ( SCSIData[ 10 ] << 8 ) | ( SCSIData[ 11 ] );
  414.         addr += blocks;
  415.  
  416.         if (( addr >= TOCTrackAddr[ Track - 1 ] ) && ( addr < TOCTrackAddr[ Track ] )) {
  417.             command2.B2 = ( addr & 0xFF000000 ) >> 24;
  418.             command2.B3 = ( addr & 0x00FF0000 ) >> 16;
  419.             command2.B4 = ( addr & 0x0000FF00 ) >> 8;
  420.             command2.B5 = ( addr & 0x000000FF );
  421.  
  422.             addr = TOCTrackAddr[ TOCNumTracks ] - TOCTrackAddr[ Track - 1 ] - 1;
  423.  
  424.             command2.B6 = ( addr & 0xFF000000 ) >> 24;
  425.             command2.B7 = ( addr & 0x00FF0000 ) >> 16;
  426.             command2.B8 = ( addr & 0x0000FF00 ) >> 8;
  427.             command2.B9 = ( addr & 0x000000FF );
  428.  
  429.             DoSCSICmd(( UBYTE * )SCSIData, MAX_DATA_LEN, ( UBYTE * )&command2, sizeof( command2 ), SCSIF_READ | SCSIF_AUTOSENSE );
  430.         }
  431.     }
  432. }
  433.  
  434. /*
  435.  *    Read the info of the current CD. Info read is:
  436.  *
  437.  *    Track playing.
  438.  *    Index playing.
  439.  *    Track time played.
  440.  *    Track time to go.
  441.  *
  442.  *    Also reset timer if necessary.
  443.  */
  444. Prototype VOID SCSI_ReadCDInfo( BOOL );
  445.  
  446. VOID SCSI_ReadCDInfo( BOOL timer )
  447. {
  448.     static struct SCSICmd10 command = { SCSI_CMD_READSUBCHANNEL, 0, 0x40, 0, 0, 0, 0, 0, 0, 0 };
  449.     LONG            microsleft = 950000;
  450.     ULONG            addr;
  451.  
  452.     command.B2 = 0x40;
  453.     command.B3 = 1;
  454.     command.B6 = 0;
  455.     command.B7 = 255;
  456.     command.B8 = 255;
  457.  
  458.     if ( DoSCSICmd(( UBYTE * )SCSIData, MAX_DATA_LEN, ( UBYTE * )&command, sizeof( command ), SCSIF_READ | SCSIF_AUTOSENSE )) {
  459.         if ( Status != SCSI_STAT_NO_DISK ) {
  460.             SCSI_ReadTOC();
  461.             /*
  462.              *    Disable the Edit CD menu and
  463.              *    update visuals..
  464.              */
  465.             if ( WO_Player ) {
  466.                 DisableMenu( WO_Player, ID_EDIT, TRUE );
  467.                 SetGadgetAttrs(( struct Gadget * )GO_Title, Player, NULL, INFO_TextFormat, "<NO DISK>", TAG_END );
  468.                 SetGadgetAttrs(( struct Gadget * )GO_TrackTitle, Player, NULL, INFO_TextFormat, "", TAG_END );
  469.             }
  470.             Status = SCSI_STAT_NO_DISK;
  471.         }
  472.     } else if (( SCSIData[ 1 ] == 0x11 ) || ( SCSIData[ 1 ] == 0x12 )) {
  473.         if ( Status == SCSI_STAT_NO_DISK )
  474.             SCSI_ReadTOC();
  475.         /*
  476.          *    Update disk status.
  477.          */
  478.         if ( SCSIData[ 1 ] == 0x11 ) Status = SCSI_STAT_PLAYING;
  479.         else                 Status = SCSI_STAT_PAUSED;
  480.  
  481.         /*
  482.          *    Update track number.
  483.          */
  484.         if ( SCSIData[ 6 ] != Track ) {
  485.             Track = TrackID = SCSIData[ 6 ];
  486.             SetGadgetAttrs(( struct Gadget * )GO_TrackTitle, Player, NULL, INFO_TextFormat, &DiskTracks[ Track - 1 ][ 0 ], TAG_END );
  487.         }
  488.  
  489.         /*
  490.          *    Update index number.
  491.          */
  492.         IndexID = SCSIData[ 7 ];
  493.  
  494.         /*
  495.          *    Pickup and update title time.
  496.          */
  497.         addr = ( SCSIData[ 12 ] << 24 ) | ( SCSIData[ 13 ] << 16 ) | ( SCSIData[ 14 ] << 8 ) | ( SCSIData[ 15 ] );
  498.  
  499.         TimeIDA = addr / 75 / 60;
  500.         TimeIDB = ( addr / 75 ) % 60;
  501.  
  502.         microsleft = ( Status == SCSI_STAT_PLAYING ) ? (( 75 - addr % 75 ) * 13333 + 1000 ) : 999000;
  503.  
  504.         /*
  505.          *    Pickup and update time to go.
  506.          */
  507.         addr = ( SCSIData[ 8 ] << 24 ) | ( SCSIData[ 9 ] << 16 ) | ( SCSIData[ 10 ] << 8 ) | ( SCSIData[ 11 ] );
  508.         addr = TOCTrackAddr[ SCSIData[ 6 ]] - addr;
  509.  
  510.         if ( addr > 80 ) microsleft = (( addr < 75 ) ? addr : 74 ) * 13333 + 100;
  511.  
  512.         TogoIDA = addr / 75 / 60;
  513.         TogoIDB = ( addr / 75 ) % 60;
  514.  
  515.     } else if ( Status != SCSI_STAT_STOPPED ) {
  516.         if ( Status == SCSI_STAT_NO_DISK )
  517.             SCSI_ReadTOC();
  518.         /*
  519.          *    Reset all information.
  520.          */
  521.         Track = 0;
  522.         TrackID = IndexID = TimeIDA = TimeIDB = TogoIDA = TogoIDB = 0;
  523.         Status = SCSI_STAT_STOPPED;
  524.         SetGadgetAttrs(( struct Gadget * )GO_Title,     Player, NULL, INFO_TextFormat, DiskName, TAG_END );
  525.         SetGadgetAttrs(( struct Gadget * )GO_TrackTitle, Player, NULL, INFO_TextFormat, Artist,   TAG_END );
  526.     }
  527.     /*
  528.      *    Visual update.
  529.      */
  530.     SetGadgetAttrs(( struct Gadget * )GO_Track, Player, NULL, INDIC_Level, TrackID, TAG_END );
  531.     SetGadgetAttrs(( struct Gadget * )GO_Index, Player, NULL, INDIC_Level, IndexID, TAG_END );
  532.     SetGadgetAttrs(( struct Gadget * )GO_TimeA, Player, NULL, INDIC_Level, TimeIDA, TAG_END );
  533.     SetGadgetAttrs(( struct Gadget * )GO_TimeB, Player, NULL, INDIC_Level, TimeIDB, TAG_END );
  534.     SetGadgetAttrs(( struct Gadget * )GO_TogoA, Player, NULL, INDIC_Level, TogoIDA, TAG_END );
  535.     SetGadgetAttrs(( struct Gadget * )GO_TogoB, Player, NULL, INDIC_Level, TogoIDB, TAG_END );
  536.     /*
  537.      *    Re-trigger the timer.
  538.      */
  539.     if ( timer ) TriggerTimer( microsleft );
  540. }
  541.